#pragma once // 防止头文件被重复包含
#include <sys/socket.h>
#include <iostream>
#include <string>
#include "char_tool.hpp"
#include <vector>
#include <pthread.h>
#include "log.hpp"
#include <sstream>
#include <unordered_map>
#include <sys/stat.h>
#include <algorithm> // 包含 std::transform
#include <cctype>    // 包含 std::toupper
#include <sys/fcntl.h>
#include <sys/sendfile.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>

#define LINE_BREAKS "\r\n"
#define SET ": "
#define WWWROOT "wwwroot" // 根目录
#define HOME "test.html" // web首页文件
#define HTTP_VERSION "HTTP/1.0"
#define ENV "url_parameter"      // 环境变量的k值 ，GET方法的请求的参数通过环境变量传给子进程
#define ENV_M "request_method"   // 环境变量，请求的方法
#define ENV_L "request_len"      // 环境变量，正文的长度
#define ERR_H "wwwroot/err.html" // 返回错误页面的路径
// 协议解析

class http_request
{ // 请求
public:
  std::string request_line;                // 请求行
  std::vector<std::string> request_Header; // 请求报头
  std::string request_null_line;           // 请求空行
  std::string request_body;                // 请求正文

  bool _if_cgi; // 是否需要用CGI机制处理数据

  // 解析之后的结果
  // 把请求行打散
  std::string _method;       // 请求的方法
  std::string _url;          // 路径，可能会带参
  std::string _http_version; // 对端主机版本

  // 请求报头的k_v结构
  std::unordered_map<std::string, std::string> kv_Header;

  // 执行完Read_http_request()函数后，上述字段都会被填充

  int body_line; // 正文长度

  // GET方法请求可能会带参
  std::string _url_path;      // 资源路径
  std::string _url_parameter; // url参数
  bool if_url_parameter;      // url是否带参

  http_request()
      : _if_cgi(false), body_line(0), if_url_parameter(false)
  {
  }
};

class http_response
{ // 响应
public:
  std::string response_line;                // 状态行
  std::vector<std::string> response_Header; // 响应报头
  std::string response_null_line;           // 空行
  std::string response_body;                // 响应正文,CGI处理的数据被放到响应正文里了

  int _code; // 状态码

  int _fd; // 系统中的文件,即HTTP要打开的文件

  long _body_size;        // 响应正文的大小
  std::string _file_type; // 响应文件的类型

  http_response()
      : _code(-1), _fd(-1), _body_size(-1)
  {
  }

  ~http_response()
  {
    close(_fd);
  }
};

class http_dispose // 处理请求和响应的类
{

private:
  int _fd;               // 网络文件描述符
  http_request _http_q;  // 请求
  http_response _http_p; // 响应
  bool _if_r;            // 判断是否读取成功的标志位

private:
  void git_request_line() // 获取请求行
  {
    int i = toll::gie_rows(_fd, _http_q.request_line);
    if (i == -1)
    {
      _if_r = false;
      LOG(ERROR, "请求行读取失败");
    }
  }

  void git_request_Header() // 获取请求报头
  {
    std::string str;
    while (true)
    {
      str.clear();
      int i = toll::gie_rows(_fd, str);
      if (-1 == i)
      {
        _if_r = false;
        LOG(ERROR, "请求报头获取失败");
        break;
      }

      if (str == "\n") // 隐式类型转换
      {
        _http_q.request_null_line.push_back(str[0]);
        break;
      }

      _http_q.request_Header.push_back(str);
    }
  }

  void scatter_request_line() // 把请求行打散
  {
    std::istringstream ss(_http_q.request_line);
    // LOG(INFO, "执行完打散函数了");
    ss >> _http_q._method >> _http_q._url >> _http_q._http_version;                                     //_http_q.request_line中的"会被>>处理掉"
    std::transform(_http_q._method.begin(), _http_q._method.end(), _http_q._method.begin(), ::toupper); // 小写转大写，::代表在全局域搜索
  }
  void scatter_request_Header() // 把请求报头打散
  {
    std::string k;
    std::string v;
    for (int i = 0; i < (int)_http_q.request_Header.size(); i++)
    {

      toll::char_kv(_http_q.request_Header[i], k, v, SET);
      _http_q.kv_Header.insert({k, v});
    }
  }

  bool if_read_request_body() // 是否读取请求正文
  {
    if (_http_q._method == "POST") // 是否读取请求正文
    {
      if (_http_q.kv_Header.find("Content-Length") != _http_q.kv_Header.end())
      {
        _http_q.body_line = std::stoi(_http_q.kv_Header["Content-Length"]);
        return true;
      }
    }

    return false;
  }

  void read_request_body() // 读取请求正文
  {
    if (if_read_request_body())
    {
      //  LOG(INFO, "要读取请求正文了");
      char ch = 'a';
      int count = _http_q.body_line;
      while (count)
      {
        if (recv(_fd, &ch, 1, 0) == -1)
        {
          _if_r = false;
          break;
        }
        _http_q.request_body += ch;
        count--;
      }
      LOG(INFO, _http_q.request_body.c_str());
    }
  }

  void cgi_dispose_request_y() // 用CGI机制处理数据
  {
    //  LOG(INFO, "进入CGI机制的函数了...");
    // 建立信道,相对于父进程来说
    int r_channel[2] = {-1, -1}; // 读信道
    int w_channel[2] = {-1, -1}; // 写信道

    if (0 != pipe(r_channel) || 0 != pipe(w_channel)) // 创建匿名管道
    {
      LOG(ERROR, "信道建立失败，父子进程无法通信，无法用CGI机制完成本次请求");
      _http_p._code = 500;
      return;
    }
    // 创建子进程
    pid_t id = fork();

    if (id == 0) // 子进程
    {
      LOG(INFO, "正在执行子进程");
      close(r_channel[0]);
      close(w_channel[1]);

      if (setenv(ENV_M, _http_q._method.c_str(), 1) == -1)
      {
        LOG(ERROR, "环境变量添加失败，子进程可能不知道请求的方法，可能无法用CGI机制处理请求");
        exit(500);
      }
      if (setenv(ENV_L, (std::to_string(_http_q.body_line)).c_str(), 1) == -1)
      {
        LOG(ERROR, "环境变量添加失败，子进程可能不知道请求的方法，可能无法用CGI机制处理请求");
        exit(500);
      }

      // 在程序替换之前，需要把子进程的读信道和写信道分别重定向到标准输出0，标准错误1中
      if (_http_q.if_url_parameter) // url带参说明它是一个GET请求，用环境变量传递数据
      {
        if (setenv(ENV, _http_q._url_parameter.c_str(), 1) == -1)
        {
          LOG(ERROR, "环境变量添加失败，子进程拿不到数据，可能无法用CGI机制处理请求");
          exit(500);
        }
      }
      // LOG(INFO, "环境变量添加完毕");

      dup2(w_channel[0], 0); // 0号文件描述符，从信道里读
      dup2(r_channel[1], 1); // 1号文件描述符，向信道里写
      // 代码和数据均会被替换，但内核数据不会被替换，包括文件描述符
      if (execl(_http_q._url_path.c_str(), _http_q._url_path.c_str(), nullptr) == -1)
      {
        LOG(ERROR, "程序替换失败，无法用CGI机制处理本次请求");
        exit(500);
      }
    }
    else if (-1 == id)
    {
      // 创建子进程失败
      LOG(ERROR, "子进程创建失败，无法用CGI机制处理本次请求");
      _http_p._code = 500;
      return;
    }
    else // 父进程
    {
      //  std::cerr << "子进程的pid是：" << id << std::endl;
      close(r_channel[1]);
      close(w_channel[0]);

      if (_http_q.if_url_parameter == false) // url不带参，在这里说明该请求是一个POST请求
      {
        int max = _http_q.body_line;
        for (int i = 0; i < max; i++)
        {
          write(w_channel[1], &_http_q.request_body[i], 1); // 把请求正文写入管道
        }
      }

      char ch = 'x';
      while (read(r_channel[0], &ch, 1) == 1) // 死循环读取数据，管道没有数据阻塞，子进程发送数据完毕退出，文件描述符也会关闭，父进程就会读0，然后退出循环
      {
        _http_p.response_body += ch;
      }

      close(r_channel[0]); // 关闭文件描述符，在网络通信中，文件描述符也是一种很重要的资源
      close(w_channel[1]);

      int code; // 子进程的退出状态
      waitpid(id, &code, 0);
      if (WIFEXITED(code)) // 如果子进程是正常退出
      {
        _http_p._code = WEXITSTATUS(code); // 返回子进程的退出码
      }
      else
      {
        _http_p._code = 500;
        return; // 不然返回错误的状态码
      }
    }
  }

  void cgi_dispose_request_n() // 不用CGI处理数据，只打开文件就行
  {
    int fd = -1;
   
    if (_http_p._code == 404)
    {
      fd = open(ERR_H, O_RDONLY);
    }
    else
    {
      fd = open(_http_q._url_path.c_str(), O_RDONLY);
    }

    if (-1 == fd)
    {
      LOG(WARNING, "文件打开失败，对端要访问的资源不存在,代码主逻辑有漏洞");
      _http_p._code = 404;
      return;
    }
    _http_p._fd = fd;

    return;
  }

  void Build_response() // 构建响应的函数
  {
    // 添加响应行
    _http_p.response_line += HTTP_VERSION;
    _http_p.response_line += " ";
    _http_p.response_line += std::to_string(_http_p._code);
    _http_p.response_line += " ";
    _http_p.response_line += state_kv(_http_p._code);
    _http_p.response_line += LINE_BREAKS;

    // 添加响应报头
    std::string header_line;
    header_line += "Content-Type: ";
    header_line += file_type_kv(_http_p._file_type); // 添加报头中的文件类型
    header_line += LINE_BREAKS;
    _http_p.response_Header.push_back(header_line);

    header_line = "Content-Length: ";
    header_line += std::to_string(_http_p._body_size); // 添加报头中文件的大小
    header_line += LINE_BREAKS;
    _http_p.response_Header.push_back(header_line);

    // 添加响应空行
    _http_p.response_null_line += LINE_BREAKS;
  }

public:
  http_dispose(int fd)
      : _fd(fd), _if_r(true)
  {
  }

  ~http_dispose()
  {
    close(_fd);
  }

  bool Read_http_request() // 读取请求
  {
    git_request_line();
    if (_if_r == false)
      return false;
    git_request_Header();
    if (_if_r == false)
      return false;

    scatter_request_line();
    scatter_request_Header();

    read_request_body();
    if (_if_r == false)
    {
      return false;
    }
    else
    {
      return true;
    }
  }

  void analyse_http_request() // 分析请求
  {
    if (_http_q._method != "GET" && _http_q._method != "POST")
    {
      // 不受与处理
      // 返回构建相关提示错误的响应
      _http_p._code = 405;
      return;
    }

    if (_http_q._method == "GET") // 分析请求带参情况
    {
      // url带参情况
      size_t pos = 0;
      pos = _http_q._url.find("?");
      if (pos != std::string::npos)
      {
        // GET方法请求带参
        _http_q.if_url_parameter = true; // 请求带参

        std::string str1;
        std::string str2;
        toll::char_kv(_http_q._url, str1, str2, "?");
        _http_q._url_path = str1;      // 填充路径
        _http_q._url_parameter = str2; // 填充GET方法中的参数
      }
      else
      {
        // GET方法请求不带参
        _http_q._url_path = _http_q._url;
      }
    }
    else
    {
      std::string str1;
      std::string str2;
      toll::char_kv(_http_q._url, str1, str2, "?"); // 防止POST传参，对于POST的参数不受与请求
      _http_q._url_path = str1;                     // 填充路径
    }

    // web根目录的处理
    std::string tmp(WWWROOT);
    tmp += _http_q._url_path;
    _http_q._url_path = tmp;
    if (_http_q._url_path[_http_q._url_path.size() - 1] == '/')
    {
      std::string str(HOME);
      _http_q._url_path += str;
    }

    // 判断资源是否存在
    struct stat sb;
    if (stat(_http_q._url_path.c_str(), &sb) == 0) // 说明该路径或文件存在
    {
      if (S_ISDIR(sb.st_mode)) // 请求要访问的是目录
      {
        // 该目录下可能会有很多文件，默认访问首页文件
        _http_q._url_path += "/";
        _http_q._url_path += HOME;
        stat(_http_q._url_path.c_str(), &sb);
      }
      // 判断是否要用CGI机制
      if (_http_q._method == "POST" || (_http_q._method == "GET" && _http_q.if_url_parameter))
      {
        if ((sb.st_mode & S_IXUSR) || (sb.st_mode & S_IXGRP) || (sb.st_mode & S_IXOTH)) // 检测拥有者，用户组，其他用户是否有可执行权限
        {
          // 拥有可执行权限
          // 特殊处理， 也需要CGI机制处理数据
          _http_q._if_cgi = true;
        }
      }
    }
    else
    {
      // 访问的资源不存在
      // 构建错误响应
      _http_p._code = 404;
      _http_q._url_path = ERR_H;
    }

    if (_http_q._method == "GET")
    {
      _http_p._body_size = sb.st_size;            // 请求要访问的文件（要响应回去的文件）的大小
      size_t pos1 = _http_q._url_path.rfind("."); // 要响应文件的类型
      if (pos1 != std::string::npos)
      {
        _http_p._file_type = _http_q._url_path.substr(pos1);
      }
      else
      {
        _http_p._file_type = ".html";
      }
    }
  }

  void make_http_response() // 构建响应
  {
    if (_http_p._code == 405)
      return;

    // 是否要用CGI机制处理请求
    if (_http_q._if_cgi)
    {
      cgi_dispose_request_y();
    }
    else
    {
      cgi_dispose_request_n();
    }

    // POST请求需要CGI程序处理完数据，才能知道要响应数据的大小
    if (_http_q._method == "POST")
    {
      _http_p._body_size = _http_p.response_body.size();
      _http_p._file_type = file_type_kv(".html"); // POST响应的类型默认是html
    }

    Build_response(); // 构建响应
  }

  void send_response() // 发送响应
  {
    if (_http_p._code == 405)
      return;
    send(_fd, _http_p.response_line.c_str(), _http_p.response_line.size(), 0); // 发送状态行
    for (int i = 0; i < (int)_http_p.response_Header.size(); i++)
    {
      send(_fd, _http_p.response_Header[i].c_str(), _http_p.response_Header[i].size(), 0); // 发送报头
    }
    send(_fd, _http_p.response_null_line.c_str(), _http_p.response_null_line.size(), 0); // 发送空行

    sendfile(_fd, _http_p._fd, nullptr, _http_p._body_size); // 发送响应正文
  }
};

class execute{  //线程要调用的类
public:

void operator()(int fd)  //仿函数
{
 thread_flow(fd);
}

private:

   void thread_flow(int fd) // 线程的执行流
{
 
  http_dispose d(fd);   
  if (d.Read_http_request())
  {
    d.analyse_http_request();
    d.make_http_response();
    d.send_response();
  }
  else
  {
    LOG(WARNING, "读取请求失败，不做处理");
  }
};

};